#include "ChemObject.hpp"
#pragma once
namespace Chemistry {
   class Element :public Object {
    public:
        Element(Element& element) :Object(element._String) {
        }
        Element(const char* str) :Object(str) {

        }
        virtual ~Element() {

        }

        const std::string& ElementString()const
        {
            return _String;
        }

        virtual std::vector<std::string> getElement() {
            std::vector<std::string> out;
            out.push_back(_String);
            return out;
        }
        virtual int getElementCount(const char* element) {
            if (_String != element)return 0;
            return 1;
        }
    };
    class ElementPair :public Object
    {
    protected:
        Element _element;
        int _count;
    public:
        ElementPair(const char* str,int cnt) :Object(""), _element(str), _count(cnt)
        {
        }
        ElementPair(Element& element, int cnt) :Object(""), _element(element), _count(cnt)
        {
            
        }
        const Element& getElement() const {
            return _element;
        }
        

        virtual ~ElementPair() {

        }
        virtual std::string getString()
        {
            if (_count == 1) {
                return _element.getString();
            }
            else 
                return _element.getString() + getCountString(_count);
        }

        virtual std::vector<std::string> getElements() {
            std::vector<std::string> out;
            out.push_back(_element.getString());
            return out;
        }
        virtual int getElementCount(const char* element) {
            if (_element.getString()!= element)return 0;
            return _count;
        }
    };

    class ElementCombine :public Object {
        std::vector<ElementPair*> _pairs;
    public:
        ElementCombine() :Object("") {

        }
        void append(ElementPair * pair) {
            _pairs.push_back(pair);
        }
        virtual ~ElementCombine() {

        }
        virtual std::string getString()
        {
            std::string out;
            
            for (size_t i = 0; i < _pairs.size(); i++)
            {
                out += _pairs[i]->getString();
            }
            return out;
        }

        virtual std::vector<std::string> getElements() {
            std::vector<std::string> out;
            for (size_t i = 0; i < _pairs.size(); i++)
            {
                out.push_back(_pairs[i]->getElement().ElementString());
            }
            
            return out;
        }
        virtual int getElementCount(const char* element) {
            for (size_t i = 0; i < _pairs.size(); i++)
            {
                if (_pairs[i]->getElement().ElementString() == element){
                    return _pairs[i]->getElementCount(element);
                }
            }
            return 0;
        }


    };

    class ElementCombinePair :public Object {
    protected:
        ElementCombine _combine;
        int _count;
    public:
        ElementCombinePair(ElementCombine &combine,int cnt) :Object(""), _combine(combine), _count(cnt) {

        }
        virtual ~ElementCombinePair() {

        }

        virtual std::string getString()
        {
            if (_count == 1) 
                return _combine.getString();
            else
                return "("+ _combine.getString() +")" + getCountString(_count);
        }

        virtual std::vector<std::string> getElements() {
            std::vector<std::string> out = _combine.getElements();
            return out;
        }
        virtual int getElementCount(const char* element) {
            return _count* _combine.getElementCount(element);
        }
    };

    class MolecularFormula :public Object {
    protected:
        std::vector<ElementCombinePair*> _pairs;

    public:
        MolecularFormula() :Object(""){

        }
        virtual ~MolecularFormula() {

        }
        void append(ElementCombinePair* pair) {
            _pairs.push_back(pair);
        }
        virtual std::string getString()
        {
            std::string out;

            for (size_t i = 0; i < _pairs.size(); i++)
            {
                out += _pairs[i]->getString();
            }
            return out;
        }

        virtual std::vector<std::string> getElements() {
            std::vector<std::string> out;
            
            for (size_t i = 0; i < _pairs.size(); i++)
            {
                std::vector<std::string> elements = _pairs[i]->getElements();
                for (size_t j = 0; j < elements.size(); j++)
                {
                    std::vector<std::string>::iterator it = std::find(out.begin(), out.end(), elements[j]);
                    if (it == out.end())
                    {
                        out.push_back(elements[j]);
                    }
                   
                }
            }

            return out;
        }
        virtual int getElementCount(const char* element) {
            int totalCount = 0;
            for (size_t i = 0; i < _pairs.size(); i++)
            {
                totalCount += _pairs[i]->getElementCount(element);
            }
            return totalCount;
        }
    };
}
